<template>
  <div id="design-area"
       ref="designArea"
       v-drop="{ ondrop: ondrop,dropable:true }">
    <component v-for="comp in components"
               class="design-area-component"
               :is="comp.tagName"
               :key="comp.id"
               v-model="comp.attributes.value"
               v-bind="{ ...comp.attributes }"
               @click="onClickComponent(comp)"
               @contextmenu="onContextmenu(comp,$event)"
               :style="getStyle(comp)"
               v-drag="{ attr: comp,draggable:!(comp.tagName === 'el-container')}">
      <span v-if="typeof comp.slots ==='string'">{{comp.slots}}</span>
      <component v-else
                 v-for="slot in comp.slots"
                 v-bind="slot.attributes"
                 :key="slot.id"
                 :is="slot.tagName">
      </component>
    </component>
    <ContextMenu v-if="showContextMenu"
                 :id="curCompId"
                 :comtextMenuPos="comtextMenuPos"
                 @emitContextMenuFunc="emitContextMenuFunc" />
  </div>
</template>

<script>
import { onMounted, ref, getCurrentInstance } from 'vue';
import { v4 as uuidv4 } from 'uuid';
import { ElMessage } from 'element-plus';
import ContextMenu from '@/components/ContextMenu.vue';

export default {
  components: {
    ContextMenu,
  },
  setup () {
    const components = ref([]);
    const designArea = ref(null);
    const comtextMenuPos = ref({ x: 0, y: 0 });
    const internalInstance = getCurrentInstance();
    const showContextMenu = ref(false);
    const curCompId = ref(0);
    let designAreaRect;

    function getComponentById (lists, id) {
      return lists.find((comp) => {
        if (comp.id === id) {
          return comp;
        }
        return false;
      });
    }
    function getComponentIndexById (lists, id) {
      return lists.findIndex((comp, index) => {
        if (comp.id === id) {
          return true;
        }
        return false;
      });
    }

    internalInstance.ctx.$mitt.on('attrChange', (attr) => {
      const {
        attrName,
        value,
        id,
        parentId,
      } = attr;
      // 区分设置时属性是否为slots内的内容
      if (!parentId) {
        const comp = getComponentById(components.value, id);
        comp.attributes[attrName] = value;
      } else {
        const parentComp = getComponentById(components.value, parentId);
        const comp = getComponentById(parentComp.slots, id);
        comp.attributes[attrName] = value;
      }
    });

    onMounted(() => {
      designAreaRect = designArea.value.getBoundingClientRect();
    });

    function onClickComponent (comp) {
      internalInstance.ctx.$mitt.emit('chooseComp', JSON.parse(JSON.stringify(comp)));
    }

    function onContextmenu (comp, event) {
      event.preventDefault();
      comtextMenuPos.value.x = event.clientX - designAreaRect.left;
      comtextMenuPos.value.y = event.clientY - designAreaRect.top;
      showContextMenu.value = true;
      curCompId.value = comp.id;
    }

    function emitContextMenuFunc (type, id) {
      const index = getComponentIndexById(components.value, id);
      switch (type) {
        case 0:
          components.value.splice(index, 1);
          break;

        default:
          break;
      }
      showContextMenu.value = false;
    }

    function ondrop (event, data, distance) {
      if (data.id) {
        const comp = getComponentById(components.value, data.id);
        const { distanceX, distanceY } = distance;
        comp.styles.x = event.clientX - distanceX;
        comp.styles.y = event.clientY - distanceY;
      } else {
        // 布局类容器仅能放置一次
        const findResult = components.value.find((comp) => comp.tagName === 'el-container');
        if (data.tagName === 'el-container' && findResult) {
          ElMessage.warning({
            message: '布局类容器仅能放置一次!',
            type: 'warning',
          });
          return;
        }
        data.styles.x = event.clientX - designAreaRect.left;
        data.styles.y = event.clientY - designAreaRect.top;
        data.id = uuidv4();
        // 按钮类slots为字符串
        if (Array.isArray(data.slots)) {
          data.slots.forEach((slot) => {
            slot.id = uuidv4();
          });
        }
        components.value.push(JSON.parse(JSON.stringify(data)));
      }
    }

    function getStyle (comp) {
      return {
        left: `${comp.styles.x}px`,
        top: `${comp.styles.y}px`,
        width: `${comp.styles.width}px`,
        height: `${comp.styles.height}px`,
        background: comp.styles.background,
      };
    }

    return {
      components,
      ondrop,
      ondragover,
      getStyle,
      designArea,
      comtextMenuPos,
      onClickComponent,
      onContextmenu,
      emitContextMenuFunc,
      showContextMenu,
      curCompId,
    };
  },
};
</script>

<style lang="scss" scoped>
#design-area {
  width: 100%;
  height: 100%;
  background: hsl(0deg 0% 98%);
  position: relative;
  .design-area-component {
    position: absolute;
  }
  .design-area-container {
    width: 100%;
    height: 100%;
    background: hsl(0deg 0% 98%);
  }
}
</style>
